/*
 * Copyright (c) 2018-2999 广州亚米信息科技有限公司 All rights reserved.
 *
 * https://www.gz-yami.com/
 *
 * 未经允许，不可做商业用途！
 *
 * 版权所有，侵权必究！
 */

package com.yami.shop.api.controller;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.security.oauth2.provider.TokenRequest;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.xkcoding.http.HttpUtil;
import com.xkcoding.http.config.HttpConfig;
import com.xkcoding.http.support.hutool.HutoolImpl;
import com.yami.shop.api.config.AuthMyGitlabRequest;
import com.yami.shop.api.config.AuthStateRedisCache;
import com.yami.shop.api.security.WebAuthenticationToken;
import com.yami.shop.bean.model.User;
import com.yami.shop.bean.model.UserThrid;
import com.yami.shop.bean.param.UserRegisterParam;
import com.yami.shop.security.service.YamiUser;
import com.yami.shop.security.service.YamiUserDetailsService;
import com.yami.shop.service.UserService;
import com.yami.shop.service.UserThridService;

import io.swagger.annotations.Api;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.enums.scope.AuthBaiduScope;
import me.zhyd.oauth.enums.scope.AuthCodingScope;
import me.zhyd.oauth.enums.scope.AuthFacebookScope;
import me.zhyd.oauth.enums.scope.AuthGiteeScope;
import me.zhyd.oauth.enums.scope.AuthGithubScope;
import me.zhyd.oauth.enums.scope.AuthGitlabScope;
import me.zhyd.oauth.enums.scope.AuthGoogleScope;
import me.zhyd.oauth.enums.scope.AuthHuaweiScope;
import me.zhyd.oauth.enums.scope.AuthMicrosoftScope;
import me.zhyd.oauth.enums.scope.AuthWeiboScope;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.AuthAlipayRequest;
import me.zhyd.oauth.request.AuthAliyunRequest;
import me.zhyd.oauth.request.AuthBaiduRequest;
import me.zhyd.oauth.request.AuthCodingRequest;
import me.zhyd.oauth.request.AuthCsdnRequest;
import me.zhyd.oauth.request.AuthDingTalkRequest;
import me.zhyd.oauth.request.AuthDouyinRequest;
import me.zhyd.oauth.request.AuthElemeRequest;
import me.zhyd.oauth.request.AuthFacebookRequest;
import me.zhyd.oauth.request.AuthFeishuRequest;
import me.zhyd.oauth.request.AuthGiteeRequest;
import me.zhyd.oauth.request.AuthGithubRequest;
import me.zhyd.oauth.request.AuthGitlabRequest;
import me.zhyd.oauth.request.AuthGoogleRequest;
import me.zhyd.oauth.request.AuthHuaweiRequest;
import me.zhyd.oauth.request.AuthKujialeRequest;
import me.zhyd.oauth.request.AuthLinkedinRequest;
import me.zhyd.oauth.request.AuthMeituanRequest;
import me.zhyd.oauth.request.AuthMiRequest;
import me.zhyd.oauth.request.AuthMicrosoftRequest;
import me.zhyd.oauth.request.AuthOschinaRequest;
import me.zhyd.oauth.request.AuthPinterestRequest;
import me.zhyd.oauth.request.AuthQqRequest;
import me.zhyd.oauth.request.AuthRenrenRequest;
import me.zhyd.oauth.request.AuthRequest;
import me.zhyd.oauth.request.AuthStackOverflowRequest;
import me.zhyd.oauth.request.AuthTaobaoRequest;
import me.zhyd.oauth.request.AuthTeambitionRequest;
import me.zhyd.oauth.request.AuthToutiaoRequest;
import me.zhyd.oauth.request.AuthTwitterRequest;
import me.zhyd.oauth.request.AuthWeChatEnterpriseQrcodeRequest;
import me.zhyd.oauth.request.AuthWeChatMpRequest;
import me.zhyd.oauth.request.AuthWeChatOpenRequest;
import me.zhyd.oauth.request.AuthWeiboRequest;
import me.zhyd.oauth.request.AuthXmlyRequest;
import me.zhyd.oauth.utils.AuthScopeUtils;
import me.zhyd.oauth.utils.AuthStateUtils;

@Controller
@RequestMapping("/oauth")
@Api(tags = "用户登陆认证相关接口")
@Slf4j
public class AuthController {

	@Autowired
	private UserService userService;

	@Autowired
	private YamiUserDetailsService yamiUserDetailsService;

	@Autowired
	private UserThridService userThridService;
	
	@Value("${http.proxy.host}")
	private String proxyHost;
	
	@Value("${http.proxy.port}")
	private Integer proxyPort;
	
	@Value("${login.callback.redirect}")
	private String redirect;

	@Autowired
	@Lazy
	private AuthorizationServerTokenServices yamiTokenServices;

	@Autowired
	private AuthStateRedisCache stateRedisCache;
	
	@RequestMapping("/render/{source}")
	@ResponseBody
	public void renderAuth(@PathVariable("source") String source, HttpServletResponse response) throws IOException {
		log.info("进入render：" + source);
		AuthRequest authRequest = getAuthRequest(source);
		String authorizeUrl = authRequest.authorize(AuthStateUtils.createState());
		log.info(authorizeUrl);
		response.sendRedirect(authorizeUrl);
	}

	/**
	 * oauth平台中配置的授权回调地址，以本项目为例，在创建github授权应用时的回调地址应为：http://127.0.0.1:8443/oauth/callback/github
	 */
	@RequestMapping("/callback/{source}")
	public ModelAndView login(@PathVariable("source") String source, AuthCallback callback, HttpServletRequest request,
			HttpServletResponse httpresponse) {
		log.info("进入callback：" + source + " callback params：" + JSONObject.toJSONString(callback));
		AuthRequest authRequest = getAuthRequest(source);
		AuthResponse<AuthUser> response = authRequest.login(callback);
		log.info("登陆返回：" + JSONObject.toJSONString(response));
		if (response.ok()) {
			log.info("callback result:" + response.getData());
			String email = response.getData().getEmail();
			String thridId = response.getData().getUuid();
			QueryWrapper queryWrapper = new QueryWrapper<User>();
			queryWrapper.eq("user_mail", email);
			User user = userService.getOne(queryWrapper);
			if (user == null) {
				log.info("数据库不存在当前用户，添加一条用户!");
				UserRegisterParam userRegisterParam = new UserRegisterParam();
				userRegisterParam.setUserMail(email);
				userService.insertUser(userRegisterParam);
				user = userService.getOne(queryWrapper);
			}
			queryWrapper = new QueryWrapper<UserThrid>();
			queryWrapper.eq("user_id", user.getUserId());
			queryWrapper.eq("type", source);
			UserThrid entity = userThridService.getOne(queryWrapper);
			if (entity == null) {
				log.info("数据库不存在绑定记录，添加一条记录!");
				entity = new UserThrid();
				entity.setCreateDate(new Date());
				entity.setUserId(user.getUserId());
				entity.setThridId(thridId);
				entity.setType(source);
				userThridService.save(entity);
			}
			YamiUser loadedUser = yamiUserDetailsService.loadUserByUserMail(user.getUserMail(),
					user.getLoginPassword());
			WebAuthenticationToken result = new WebAuthenticationToken(loadedUser, user.getLoginPassword());
			String token = getToken(result);
			log.info("平台登陆：" + JSONObject.toJSONString(token));
			Map<String, Object> data = new HashMap<>();
			data.put("token", token);
			return new ModelAndView("redirect:"+redirect, data);
		}

		Map<String, Object> map = new HashMap<>(1);
		map.put("errorMsg", response.getMsg());
		return new ModelAndView("error", map);
	}

	private String getToken(Authentication authentication) {
		TokenRequest tokenRequest = new TokenRequest(null, null, null, null);
		// 简化
		OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(new BaseClientDetails());
		OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, authentication);
		OAuth2AccessToken oAuth2AccessToken = yamiTokenServices.createAccessToken(oAuth2Authentication);
		return oAuth2AccessToken.getValue();
	}

	/**
	 * 根据具体的授权来源，获取授权请求工具类
	 *
	 * @param source
	 * @return
	 */
	private AuthRequest getAuthRequest(String source) {
		HttpUtil.setHttp(new HutoolImpl());
		AuthRequest authRequest = null;
		switch (source.toLowerCase()) {
		case "dingtalk":
			authRequest = new AuthDingTalkRequest(AuthConfig.builder().clientId("").clientSecret("")
					.redirectUri("http://localhost:8443/oauth/callback/dingtalk").build());
			break;
		case "baidu":
			authRequest = new AuthBaiduRequest(AuthConfig.builder().clientId("").clientSecret("")
					.redirectUri("http://localhost:8443/oauth/callback/baidu")
					.scopes(Arrays.asList(AuthBaiduScope.BASIC.getScope(), AuthBaiduScope.SUPER_MSG.getScope(),
							AuthBaiduScope.NETDISK.getScope()))
					// .clientId("")
					// .clientSecret("")
					// .redirectUri("http://localhost:9001/oauth/baidu/callback")
					.build());
			break;
		case "github":
			authRequest = new AuthGithubRequest(AuthConfig.builder().clientId("").clientSecret("")
					.redirectUri("http://localhost:8443/oauth/callback/github")
					.scopes(AuthScopeUtils.getScopes(AuthGithubScope.values()))
					// 针对国外平台配置代理
					.httpConfig(HttpConfig.builder().timeout(15000)
							.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 10080))).build())
					.build(), stateRedisCache);
			break;
		case "gitee":
			authRequest = new AuthGiteeRequest(AuthConfig.builder().clientId("").clientSecret("")
					.redirectUri("http://127.0.0.1:8443/oauth/callback/gitee")
					.scopes(AuthScopeUtils.getScopes(AuthGiteeScope.values())).build(), stateRedisCache);
			break;
		case "weibo":
			authRequest = new AuthWeiboRequest(AuthConfig.builder().clientId("").clientSecret("")
					.redirectUri("http://dblog-web.zhyd.me/oauth/callback/weibo")
					.scopes(Arrays.asList(AuthWeiboScope.EMAIL.getScope(),
							AuthWeiboScope.FRIENDSHIPS_GROUPS_READ.getScope(),
							AuthWeiboScope.STATUSES_TO_ME_READ.getScope()))
					.build());
			break;
		case "coding":
			authRequest = new AuthCodingRequest(
					AuthConfig.builder().clientId("").clientSecret("")
							.redirectUri("http://dblog-web.zhyd.me/oauth/callback/coding").domainPrefix("")
							.scopes(Arrays.asList(AuthCodingScope.USER.getScope(),
									AuthCodingScope.USER_EMAIL.getScope(), AuthCodingScope.USER_PHONE.getScope()))
							.build());
			break;
		case "oschina":
			authRequest = new AuthOschinaRequest(AuthConfig.builder().clientId("").clientSecret("")
					.redirectUri("http://localhost:8443/oauth/callback/oschina").build());
			break;
		case "alipay":
			// 支付宝在创建回调地址时，不允许使用localhost或者127.0.0.1，所以这儿的回调地址使用的局域网内的ip
			authRequest = new AuthAlipayRequest(AuthConfig.builder().clientId("").clientSecret("").alipayPublicKey("")
					.redirectUri("https://www.zhyd.me/oauth/callback/alipay").build());
			break;
		case "qq":
			authRequest = new AuthQqRequest(AuthConfig.builder().clientId("").clientSecret("")
					.redirectUri("http://localhost:8443/oauth/callback/qq").build());
			break;
		case "wechat_open":
			authRequest = new AuthWeChatOpenRequest(AuthConfig.builder().clientId("").clientSecret("")
					.redirectUri("http://www.zhyd.me/oauth/callback/wechat").build());
			break;
		case "csdn":
			authRequest = new AuthCsdnRequest(AuthConfig.builder().clientId("").clientSecret("")
					.redirectUri("http://dblog-web.zhyd.me/oauth/callback/csdn").build());
			break;
		case "taobao":
			authRequest = new AuthTaobaoRequest(AuthConfig.builder().clientId("").clientSecret("")
					.redirectUri("http://dblog-web.zhyd.me/oauth/callback/taobao").build());
			break;
		case "google":
			authRequest = new AuthGoogleRequest(AuthConfig.builder()
//					.clientId("138693785806-9h27efv0hc1nb484238ud6hbgnmgl6qc.apps.googleusercontent.com")
//					.clientSecret("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")
//					.redirectUri("http://localhost:8443/oauth/callback/google")
					.clientId("138693785806-nir80tu0unq0c54rb815sob2anri6c21.apps.googleusercontent.com")
					.clientSecret("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")
					.redirectUri("https://h5.shenyankj.com/api/oauth/callback/google")
					.scopes(AuthScopeUtils.getScopes(AuthGoogleScope.USER_EMAIL, AuthGoogleScope.USER_PROFILE,
							AuthGoogleScope.USER_OPENID))
					// 针对国外平台配置代理
					.httpConfig(HttpConfig.builder().timeout(15000)
							.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort))).build())
					.build());
			break;
		case "facebook":
			authRequest = new AuthFacebookRequest(AuthConfig.builder().clientId("").clientSecret("")
					.redirectUri("https://justauth.cn/oauth/callback/facebook")
					.scopes(AuthScopeUtils.getScopes(AuthFacebookScope.values()))
					// 针对国外平台配置代理
					.httpConfig(HttpConfig.builder().timeout(15000)
							.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 10080))).build())
					.build());
			break;
		case "douyin":
			authRequest = new AuthDouyinRequest(AuthConfig.builder().clientId("").clientSecret("")
					.redirectUri("http://dblog-web.zhyd.me/oauth/callback/douyin").build());
			break;
		case "linkedin":
			authRequest = new AuthLinkedinRequest(AuthConfig.builder().clientId("").clientSecret("")
					.redirectUri("http://localhost:8443/oauth/callback/linkedin").scopes(null).build());
			break;
		case "microsoft":
			authRequest = new AuthMicrosoftRequest(AuthConfig.builder().clientId("").clientSecret("")
					.redirectUri("http://localhost:8443/oauth/callback/microsoft")
					.scopes(Arrays.asList(AuthMicrosoftScope.USER_READ.getScope(),
							AuthMicrosoftScope.USER_READWRITE.getScope(),
							AuthMicrosoftScope.USER_READBASIC_ALL.getScope(),
							AuthMicrosoftScope.USER_READ_ALL.getScope(),
							AuthMicrosoftScope.USER_READWRITE_ALL.getScope(),
							AuthMicrosoftScope.USER_INVITE_ALL.getScope(),
							AuthMicrosoftScope.USER_EXPORT_ALL.getScope(),
							AuthMicrosoftScope.USER_MANAGEIDENTITIES_ALL.getScope(),
							AuthMicrosoftScope.FILES_READ.getScope()))
					.build());
			break;
		case "mi":
			authRequest = new AuthMiRequest(AuthConfig.builder().clientId("").clientSecret("")
					.redirectUri("http://dblog-web.zhyd.me/oauth/callback/mi").build());
			break;
		case "toutiao":
			authRequest = new AuthToutiaoRequest(AuthConfig.builder().clientId("").clientSecret("")
					.redirectUri("http://dblog-web.zhyd.me/oauth/callback/toutiao").build());
			break;
		case "teambition":
			authRequest = new AuthTeambitionRequest(AuthConfig.builder().clientId("").clientSecret("")
					.redirectUri("http://127.0.0.1:8443/oauth/callback/teambition").build());
			break;
		case "pinterest":
			authRequest = new AuthPinterestRequest(AuthConfig.builder().clientId("").clientSecret("")
					.redirectUri("https://eadmin.innodev.com.cn/oauth/callback/pinterest")
					// 针对国外平台配置代理
					.httpConfig(HttpConfig.builder().timeout(15000)
							.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 10080))).build())
					.build());
			break;
		case "renren":
			authRequest = new AuthRenrenRequest(AuthConfig.builder().clientId("").clientSecret("")
					.redirectUri("http://127.0.0.1:8443/oauth/callback/teambition").build());
			break;
		case "stack_overflow":
			authRequest = new AuthStackOverflowRequest(AuthConfig.builder().clientId("").clientSecret("((")
					.redirectUri("http://localhost:8443/oauth/callback/stack_overflow").stackOverflowKey("").build());
			break;
		case "huawei":
			authRequest = new AuthHuaweiRequest(AuthConfig.builder().clientId("").clientSecret("")
					.redirectUri("http://127.0.0.1:8443/oauth/callback/huawei")
					.scopes(Arrays.asList(AuthHuaweiScope.BASE_PROFILE.getScope(),
							AuthHuaweiScope.MOBILE_NUMBER.getScope(), AuthHuaweiScope.ACCOUNTLIST.getScope(),
							AuthHuaweiScope.SCOPE_DRIVE_FILE.getScope(),
							AuthHuaweiScope.SCOPE_DRIVE_APPDATA.getScope()))
					.build());
			break;
		case "wechat_enterprise":
			authRequest = new AuthWeChatEnterpriseQrcodeRequest(AuthConfig.builder().clientId("").clientSecret("")
					.redirectUri("http://justauth.cn/oauth/callback/wechat_enterprise").agentId("1000003").build());
			break;
		case "kujiale":
			authRequest = new AuthKujialeRequest(AuthConfig.builder().clientId("").clientSecret("")
					.redirectUri("http://dblog-web.zhyd.me/oauth/callback/kujiale").build());
			break;
		case "gitlab":
			authRequest = new AuthGitlabRequest(AuthConfig.builder().clientId("").clientSecret("")
					.redirectUri("http://localhost:8443/oauth/callback/gitlab")
					.scopes(AuthScopeUtils.getScopes(AuthGitlabScope.values())).build());
			break;
		case "meituan":
			authRequest = new AuthMeituanRequest(AuthConfig.builder().clientId("").clientSecret("")
					.redirectUri("http://localhost:8443/oauth/callback/meituan").build());
			break;
		case "eleme":
			authRequest = new AuthElemeRequest(AuthConfig.builder().clientId("").clientSecret("")
					.redirectUri("http://dblog-web.zhyd.me/oauth/callback/eleme").build());
			break;
		case "mygitlab":
			authRequest = new AuthMyGitlabRequest(AuthConfig.builder().clientId("").clientSecret("")
					.redirectUri("http://127.0.0.1:8443/oauth/callback/mygitlab").build());
			break;
		case "twitter":
			authRequest = new AuthTwitterRequest(AuthConfig.builder().clientId("").clientSecret("")
					.redirectUri("https://threelogin.31huiyi.com/oauth/callback/twitter")
					// 针对国外平台配置代理
					.httpConfig(HttpConfig.builder().timeout(15000)
							.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 10080))).build())
					.build());
			break;
		case "wechat_mp":
			authRequest = new AuthWeChatMpRequest(
					AuthConfig.builder().clientId("").clientSecret("").redirectUri("").build());
			break;
		case "aliyun":
			authRequest = new AuthAliyunRequest(AuthConfig.builder().clientId("").clientSecret("")
					.redirectUri("http://localhost:8443/oauth/callback/aliyun").build());
			break;
		case "xmly":
			authRequest = new AuthXmlyRequest(AuthConfig.builder().clientId("").clientSecret("")
					.redirectUri("http://localhost:8443/oauth/callback/xmly").build());
			break;
		case "feishu":
			authRequest = new AuthFeishuRequest(AuthConfig.builder().clientId("").clientSecret("")
					.redirectUri("http://localhost:8443/oauth/callback/feishu").build());
			break;
		default:
			break;
		}
		if (null == authRequest) {
			throw new AuthException("未获取到有效的Auth配置");
		}
		return authRequest;
	}

}
